package ru.ifmo.croak.impl.remoting.query.team3.client;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

import com.sun.xml.internal.messaging.saaj.soap.ver1_1.Envelope1_1Impl;
import ru.ifmo.croak.api.MessageQuery;
import ru.ifmo.croak.api.QueryRequest;
import ru.ifmo.croak.api.QueryService;
import ru.ifmo.croak.impl.remoting.query.team3.RemoteMessageQuery;
import ru.ifmo.croak.impl.remoting.query.team3.RemoteMessageQueryImpl;
import ru.ifmo.croak.impl.remoting.query.team3.RemoteServer;
import ru.ifmo.croak.impl.remoting.query.team3.ServerQuery;
import ru.ifmo.croak.util.CroakFactory;

/**
 * Implementation of {@link QueryService}. It does the following actions:
 * <ul>
 * <li>Connects to remote side with TCP/IP socket connection.
 * <li>Performs the actual queries on one of the configured hosts.
 * <li>Automatically retries queries if remote side is down until successful.
 * <li>Provides the consistent view of current messages via MessageQuery
 * interface despite failures in communications.
 * </ul>
 * 
 * @author Arina Afanasyeva
 */
public class QueryServiceImpl implements QueryService {
	private final String name;
	private final String[] hostPortArray;
    ExecutorService executor;
    volatile RemoteServer currentServer;

	public QueryServiceImpl(String name) {
		this.name = name;
		hostPortArray = CroakFactory.getInstance().getProperty(this.name + ".query.remote")
				.split(",");

        executor = Executors.newSingleThreadExecutor();
	}


	/**
	 * {@inheritDoc}
	 * 
	 * @param request the given request
	 * @return the new query for a given request
	 * @throws IllegalStateException
	 *             if the <code>request</code> cannot be created at this time
	 *             due to capacity restrictions
	 */
	@Override
	public MessageQuery createQuery(QueryRequest request) {
		RemoteMessageQueryImpl created = new RemoteMessageQueryImpl(this, request);
		return created;
	}

    public ServerQuery createServerQuery(RemoteMessageQuery q) {
        try {
            UnicastRemoteObject.exportObject(q, 0);
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        if (currentServer!=null) {
            try {
                return currentServer.createServerQuery(q);
            }
            catch(RemoteException e) { e.printStackTrace(); }
        }

        for(int i = 0; i < hostPortArray.length; ++i) {
            String nextServerAddress = hostPortArray[i];
            try {
                RemoteServer newServer = newServer = (RemoteServer) Naming.lookup("rmi://"
                        + nextServerAddress + "/server");

                ServerQuery sq = newServer.createServerQuery(q);
                currentServer = newServer;
                return sq;
            }
            catch (NotBoundException e1) { e1.printStackTrace(); }
            catch (MalformedURLException e1) { e1.printStackTrace(); }
            catch (RemoteException e1) { e1.printStackTrace(); }
        }

        throw new IllegalStateException("No servers responded");
    }

    public ExecutorService getExecutor() {
        return executor;
    }
}
